﻿/**

 * Copyright (c) 2015-2016, FastDev 刘强 (fastdev@163.com).

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

using OF.DistributeService.Core.Attribute;
using OF.DistributeService.Core.Client.Entity;
using OF.DistributeService.Core.Common;
using OF.DistributeService.Core.Entity;
using OF.DistributeService.Core.Exception;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using ZooKeeperNet;

namespace OF.DistributeService.Core.Client
{

    public class ClientToAppManager : IDisposable
    {
        private ZooKeeperProxy zk = null;
        private ConcurrentDictionary<Type, ClientToAppServiceMeta> dict = new ConcurrentDictionary<Type, ClientToAppServiceMeta>();
        private static ClientToAppManager instance = null;
        private static object lockObj = new object();
        private AppGroupMapInstanceList appGroupMapInstanceList = null;
        private ClientConfig clientConfig = null;
        private List<ClientToAppConfig> clientToAppConfigList = null;
        /// <summary>
        /// 根据配置的接口信息，构建接口的代理类，以及对应 OFDistributeService 服务端相关接口名、应用名等信息
        /// </summary>
        private ClientToAppManager(List<ClientToAppConfig> clientToAppConfigList)
        {
            this.clientToAppConfigList = clientToAppConfigList;
            clientConfig = ClientConfig.Get();
            if (!ZooKeeperProxy.IsValidateNodeName(clientConfig.AppName))
            {
                throw new NotCharOrNumberFormatException("clientConfig.AppName:" + clientConfig.AppName);
            }

            if (!ZooKeeperProxy.IsValidateNodeName(clientConfig.GroupName))
            {
                throw new NotCharOrNumberFormatException("GroupName:" + clientConfig.GroupName);
            }

            foreach (ClientToAppConfig clientToAppConfig in clientToAppConfigList)
            {
                if (!ZooKeeperProxy.IsValidateNodeName(clientToAppConfig.ServiceAppName))
                {
                    throw new NotCharOrNumberFormatException("ServiceAppName:" + clientToAppConfig.ServiceAppName);
                }
            }

            var watcher = new ZooKeeperSafeConnectWatcher();
            appGroupMapInstanceList = new AppGroupMapInstanceList(clientConfig);
            CommonApiCallProxyProvider commonApiCallProxyProvider = new CommonApiCallProxyProvider(appGroupMapInstanceList);
            EmitHelper emitHelper = new EmitHelper();
            zk = new ZooKeeperProxy(clientConfig.ZookeeperHostPort, new TimeSpan(0, 0, 0, clientConfig.ZookeeperSessionTimeSpan), watcher, InitZkClusterAndWatch);
            appGroupMapInstanceList.InitZk(zk);
            foreach (ClientToAppConfig clientToAppConfig in clientToAppConfigList)
            {
                foreach (ClientToAppServiceConfig clientToAppServiceConfig in clientToAppConfig.ServiceList)
                {
                    Type interfaceType = clientToAppServiceConfig.InterfaceType;
                    var apiServiceAttrMeta = OFDistributeServiceAttribute.GetApiServiceAttrMeta(interfaceType);
                    if (apiServiceAttrMeta == null || apiServiceAttrMeta.ServiceAttribute == null)
                    {
                        throw new ServiceAttributeNotDefinedException(interfaceType.FullName);
                    }
                    ClientToAppServiceMeta clientToAppServiceMeta = new ClientToAppServiceMeta
                    {
                        ClientConfig = clientConfig,
                        ClientToAppConfig = clientToAppConfig,
                        ClientToAppServiceConfig = clientToAppServiceConfig,
                        InvokerGetter = null
                    };
                    clientToAppServiceMeta.InvokerGetter = new InvokerGetter(emitHelper, clientToAppServiceMeta, commonApiCallProxyProvider, interfaceType);
                    dict.TryAdd(interfaceType, clientToAppServiceMeta);
                    List<OFDistributeServiceMethodAttribute> distributeMethodAttributeList = OFDistributeServiceMethodAttribute.GetMethodAttribute(apiServiceAttrMeta.ServiceAttribute, interfaceType);
                    MethodWithHttpMethod[] methodArray = distributeMethodAttributeList.Select(item => new MethodWithHttpMethod { ContentFormat = item.ContentFormat, HttpMethod = item.HttpMethod, MethodName = item.GetMethodName() }).ToArray();
                    clientToAppServiceConfig.SetMethodArray(methodArray);
                    clientToAppServiceConfig.SetVirtualPath(apiServiceAttrMeta.ServiceAttribute.VirtualPath);
                }
            }
            InitZkClusterAndWatch();
            CheckServiceLive checkServiceLive = new CheckServiceLive(appGroupMapInstanceList, clientConfig);
            Thread checkServiceLiveThread = new Thread(new ThreadStart(checkServiceLive.Check));
            checkServiceLiveThread.Start();
        }



        private void InitZkClusterAndWatch()
        {
            appGroupMapInstanceList.Clear();
            foreach (ClientToAppConfig clientToAppConfig in clientToAppConfigList)
            {
                appGroupMapInstanceList.Subscribe(clientToAppConfig);
            }
        }

        public class CheckServiceLive
        {
            private ClientConfig clientConfig = null;
            private AppGroupMapInstanceList appGroupMapInstanceList = null;
            public CheckServiceLive(AppGroupMapInstanceList appGroupMapInstanceList, ClientConfig clientConfig)
            {
                this.appGroupMapInstanceList = appGroupMapInstanceList;
                this.clientConfig = clientConfig;
            }

            public void Check()
            {
                while (true)
                {
                    try
                    {
                        foreach (var instanceList in appGroupMapInstanceList)
                        {
                            var cloneList = instanceList.Value.GetInstanceList();
                            foreach (var instance in cloneList)
                            {
                                instance.CheckOnLine();
                            }
                        }
                    }
                    catch(System.Exception ex)
                    {
                        Util.LogException("CheckServiceLive", ex);
                    }
                    Thread.Sleep(clientConfig.CheckServiceOnlineInterval);
                }
            }
        }

        public static readonly string SepString = new string(new char[] { (char)0x01 });
        public static string GetKey(string[] keyArray)
        {
            return string.Join(SepString, keyArray);
        }

        public static void Init(List<ClientToAppConfig> clientToAppConfigList)
        {
            bool isCreator = false;
            if (instance == null)
            {
                lock (lockObj)
                {
                    if (instance == null)
                    {
                        instance = new ClientToAppManager(clientToAppConfigList);
                        isCreator = true;
                    }
                }
            }
            if (!isCreator)
            {
                throw new ServiceDuplicateInitException();
            }
        }

        public static ClientToAppManager Get()
        {
            return instance;
        }

        public void Dispose()
        {
            if (zk != null)
            {
                zk.Dispose();
            }
        }

        /// <summary>
        /// 根据Serivce类型获取调用的代理类，其中Service类型必须同ClientToAppServiceConfig中配置的ServiceFullClassName一致（完整类名）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T GetInvoker<T>() where T : class
        {
            ClientToAppServiceMeta meta;
            if (dict.TryGetValue(typeof(T), out meta))
            {
                object invokerObject = meta.InvokerGetter.GetInvoker();
                T result = invokerObject as T;
                if (result == null)
                {
                    throw new InvokerTypeMissMatchException("Invoker type " + invokerObject.GetType() + " can not be cast to " + typeof(T));
                }
                return result;
            }
            else
            {
                throw new InvokerNotFoundException("Can not find Invoker type " + typeof(T) +",it may not inited!");
            }
        }
    }

}
